"use strict";

/**网站服务器模块，3.0版本*/

/**网站服务器模块
 *
 *一款支持多协议、多实例的网站服务器模块*/
class website {

    constructor(ConfigDataObject) {

        /**GlobalEventProcess、全局事件处理注入函数、用于注入用于处理全局发生错误的处理函数。*/
        this.GlobalEventProcess = function (CallbackFunction) {
            this.__InstanceCollect_Root.consoleLogStream.addonLog(this, "此函数即将弃用！", "warn");
            if (typeof CallbackFunction === "function") {
                this.GlobalEventJoinPoint = CallbackFunction;
            } else {
                this.__InstanceCollect_Root.consoleLogStream.addonLog(this, "形参接收到的数据不是函数！将使用默认内置的全局处理函数！", "warn");
            }
        }

        /**GlobalEventJoinPoint、全局事件处理函数接入点，默认为undefined*/
        this.GlobalEventJoinPoint = undefined;

        /**DefaultPage，默认的主页文件对象，可根据需要修改*/
        this.DefaultPage = undefined;
        /**DefaultRootPath，默认网站根路径对象，可根据需要修改*/
        this.DefaultRootPath = undefined;
        /**ServicePort，默认服务器端口数据对象，可根据需要修改http、https的端口数值，不可更改属性名*/
        this.ServicePort = {http: 80, https: 443};
        /**ServiceHost，指定要绑定的主机名称或IP地址，可以分别给（http、https）服务指定要绑定的主机名称或IP地址*/
        this.ServiceHost = {http: undefined, https: undefined};

        /**EnableHttpsMode，启用HTTPS模式，默认为false，不启用。*/
        this.EnableHttpsMode = false;
        /**EnableHttpMode，启用HTTP模式，默认为false，不启用。*/
        this.EnableHttpMode = false;
        /**SSLPrivateKey、指定的SSL私钥文件路径*/
        this.SSLPrivateKey = undefined;
        /**SSLCertificate、指定的SSL证书文件路径*/
        this.SSLCertificate = undefined;
        /**ExternalImportRouter，指定导入的外部路由对象函数组*/
        this.ExternalImportRouter = undefined;
        /**EnableHistoryRouter，启用History路由支持，默认为false，不启用*/
        this.EnableHistoryRouter = false;
        /**SpecialSupport，特别支持件引入*/
        this.SpecialSupport = undefined;
        /**NoRequiredStaticResources，不需要指定静态资源，默认为false*/
        this.NoRequiredStaticResources = false;

        /**引入外部工具库，General Tools Collection Package Kit，通用工具集合套件包，NodeJS后端版*/
        this.__GenTools = require('gtcpk-be');
        try {
            this.__ToolKit = new this.__GenTools();
        } catch (e) {
            this.__ToolKit = new this.__GenTools.GeneralToolsCollectionPackageKit();
        }
        this.__ToolKit.DataTypeDetect({
            _GlobalOperate: {
                enableDict: true, bind: {
                    String: {aliasVal: "str", aliasDsc: "为字符串类型"},
                    Array: {aliasVal: "arr", aliasDsc: "为数组类型"},
                    Object: {aliasVal: "obj", aliasDsc: "为对象类型"},
                    Boolean: {aliasVal: "boo", aliasDsc: "为布尔类型"},
                    Number: {aliasVal: "num", aliasDsc: "为数字类型"},
                    Undefined: {aliasVal: "und", aliasDsc: "未知定义类型"},
                    Function: {aliasVal: "fun", aliasDsc: "为函数类型"},
                    AsyncFunction: {aliasVal: "anf", aliasDsc: "为异步函数类型"},
                    Null: {aliasVal: "nul", aliasDsc: "为空数据类型"},
                    process: {aliasVal: "pro", aliasDsc: "Node JS Process对象"},
                    Promise: {aliasVal: "pme", aliasDsc: "Node JS Promise对象"}
                }, RetValueType: 0
            }
        });

        /**核心程序*/
        this.__Core = require('./core');

        /**自有工具集*/
        this.__MeTools = require('./tools');

        /**实例集合根*/
        this.__InstanceCollect_Root = {
            BuilderStatus: false,
            FullSuccess: false,
            closeEvent: true,
            PluginPoint: {
                webApp: require("express"),
                httpMode: require("http"),
                httpsMode: require("https"),
                HistoryRouterPlugins: require('connect-history-api-fallback')
            },
            consoleLogStream: {
                LogRecordStream: [],
                clearLog(Instance) {
                    switch (Object.prototype.toString.call(Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream)) {
                        case "[object Array]":
                            Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream.splice(0);
                            break;
                        default:
                            Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream = [];
                            break;
                    }
                },
                addonLog(Instance, LogContent, LogType) {
                    function LogDisplay(LogContent, LogType) {
                        let prefixLabel;
                        let typeValue;
                        switch (String(LogType).toLowerCase()) {
                            case "ok":
                                prefixLabel = "成功：";
                                console.log(`\x1B[32m${prefixLabel}\x1B[0m${LogContent}`);
                                typeValue = "ok";
                                break;
                            case "fail":
                                prefixLabel = "失败：";
                                console.log(`\x1B[31m${prefixLabel}\x1B[0m${LogContent}`);
                                typeValue = "fail";
                                break;
                            case "warn":
                                prefixLabel = "警告：";
                                console.log(`\x1B[33m${prefixLabel}\x1B[0m${LogContent}`);
                                typeValue = "warn";
                                break;
                            case "info":
                                prefixLabel = "信息：";
                                console.log(`\x1B[34m${prefixLabel}\x1B[0m${LogContent}`);
                                typeValue = "info";
                                break;
                            default:
                                prefixLabel = "消息：";
                                console.log(`\x1B[37m${prefixLabel}\x1B[0m${LogContent}`);
                                typeValue = "message";
                                break;
                        }
                        return [typeValue, prefixLabel];
                    }

                    let typeMsg = LogDisplay(LogContent, LogType);
                    switch (Object.prototype.toString.call(Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream)) {
                        case "[object Array]":
                            Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream.push({
                                type: typeMsg[0],
                                content: `${typeMsg[1]}${LogContent}`
                            });
                            break;
                        default:
                            Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream = [];
                            Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream.push({
                                type: typeMsg[0],
                                content: `${typeMsg[1]}${LogContent}`
                            });
                            break;
                    }
                },
                exportLog: async (Instance, saveFullPath) => {
                    let OperateValue;
                    let logWritePromise = new Promise((resolve, reject) => {
                        try {
                            if (typeof Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream !== "object") {
                                throw new Error();
                            }
                            if (Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream.length === 0) {
                                throw new Error();
                            }
                            Instance.__MeTools.BasicTools.FileStream.createWriteStream(saveFullPath, "utf-8").on("error", (vol) => {
                                reject(vol);
                            }).on("open", (vol) => {
                                for (let num in Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream) {
                                    let value = Instance.__InstanceCollect_Root.consoleLogStream.LogRecordStream[num];
                                    Instance.__MeTools.BasicTools.FileStream.write(vol, `${value.content} --${value.type}\r\n`, () => {
                                    });
                                }
                                Instance.__MeTools.BasicTools.FileStream.close(vol);
                                resolve(true);
                            });

                        } catch (e) {
                            reject(false);
                        }
                    });
                    await logWritePromise.then((val) => {
                        OperateValue = val;
                    }).catch((val) => {
                        OperateValue = val;
                    });
                    if (OperateValue) {
                        return OperateValue;
                    } else {
                        throw OperateValue;
                    }
                }
            },
            confirmPassData: undefined,
            OperateInstance: {
                webSiteInstance: {
                    /**webSiteCore，网站服务控制节点核心，不可随意操作*/
                    _webSiteCore: undefined,
                    /**webSiteControlPoint，网站服务控制节点，不可随意操作*/
                    _webSiteControlPoint: undefined,
                    /**RootDirPoint，网站服务根目录节点，不可随意操作*/
                    _RootDirPoint: undefined
                },
                clearOperate: undefined,
                confirmClear: false
            }
        }

        /**日志清除*/
        this.log_clean = () => {
            this.__InstanceCollect_Root.consoleLogStream.clearLog(this);
        }
        /**日志导出*/
        this.log_export = async (savePath) => {
            let OpStatus
            await this.__InstanceCollect_Root.consoleLogStream.exportLog(this, savePath).then((val) => {
                OpStatus = val;
            }).catch((val) => {
                OpStatus = val;
            });
            if (OpStatus === true) {
                return OpStatus
            } else {
                throw OpStatus;
            }
        }

        /**全局事件，可由GlobalEventProcess函数控制注入自定义处理函数。*/
        this.__Global_Event = require('process');
        /**全局默认事件*/
        this.__Global_Event.on('uncaughtException', (err) => {
            switch (typeof this.GlobalEventJoinPoint) {
                case "function":
                    this.GlobalEventJoinPoint(err);
                    break;
                default:
                    switch (err.code) {
                        case "EACCES":
                            console.error(`网站服务启动失败，端口：${err.port}，请检查配置的数据是否正确！如需使用80、443端口，请将此脚本置于管理员(Root)模式运行！`);
                            break;
                        case "EADDRINUSE":
                            console.error(`网站服务启动失败，端口：${err.port}，已被其他程序占用！`);
                            break;
                        default:
                            console.error(err);
                            break;
                    }
                    /**停止运行Node JS 运行环境*/
                    this.__Global_Event.exit();
                    break;
            }
        });
        switch (Object.prototype.toString.call(ConfigDataObject)) {
            case "[object Object]":
                if (this.__MeTools.ConfigureDataVerify(this, ConfigDataObject)) {
                    this.__InstanceCollect_Root.FullSuccess = true;
                    this.GlobalEventJoinPoint = this.__InstanceCollect_Root.confirmPassData.GlobalEventJoinPoint;
                } else {
                    this.__InstanceCollect_Root.FullSuccess = false;
                }
                this.__InstanceCollect_Root.BuilderStatus = true;
                break;
            case "[object Undefined]":
                this.DefaultPage = "index.html";
                let curPath = __dirname;
                if (this.__MeTools.BasicTools.FileStream.existsSync(this.__MeTools.BasicTools.PathStream.join(curPath, "node_modules", "website_express"))) {
                    this.DefaultRootPath = this.__MeTools.BasicTools.PathStream.join(curPath, "node_modules", "website_express", "guide");
                } else if (this.__MeTools.BasicTools.FileStream.existsSync(this.__MeTools.BasicTools.PathStream.join(curPath, "guide"))) {
                    this.DefaultRootPath = this.__MeTools.BasicTools.PathStream.join(curPath, "guide");
                } else {
                    throw new Error("插件模块异常，请重新安装！");
                }
                this.EnableHttpMode = true;
                this.ServicePort = 8080;
                this.EnableHistoryRouter = false;
                /**默认测试配置*/
                this.__InstanceCollect_Root.BuilderStatus = false;
                break;
            default:
                throw new Error("Error.构造数据不是对象！");
        }
    }

    /**openServer、打开web服务实例*/
    async openServer() {
        let curInstance = this;
        let tempConfig;
        let OperateStatus, OperateInfos;
        if (!curInstance.__InstanceCollect_Root.closeEvent) {
            throw ("服务实例已运行，无需重复启动。");
        }
        if (this.__InstanceCollect_Root.BuilderStatus) {
            if (!this.__InstanceCollect_Root.FullSuccess) {
                this.__InstanceCollect_Root.consoleLogStream.addonLog(this, "启动出错，请检查构造配置数据！", "Fail");
                throw ("启动出错，请检查构造配置数据！");
            }
        } else {
            tempConfig = this.__MeTools.ConfigureDataReady(this);
            if (!this.__MeTools.ConfigureDataVerify(this, tempConfig)) {
                this.__InstanceCollect_Root.consoleLogStream.addonLog(this, "启动出错，请检查配置数据！", "Fail");
                throw ("启动出错，请检查配置数据！");
            }
        }
        await this.__Core.ServiceCore(this, this.__InstanceCollect_Root.confirmPassData).then(value => {
            OperateStatus = true;
            curInstance.__InstanceCollect_Root.closeEvent = false;
            OperateInfos = value;
        }).catch(value => {
            OperateStatus = false;
            curInstance.__InstanceCollect_Root.closeEvent = true;
            OperateInfos = value;
        });
        if (OperateStatus) {
            return OperateInfos;
        } else {
            throw OperateInfos;
        }
    }

    /**closeServer、关闭web服务实例*/
    async closeServer() {
        let curInstance = this;
        let OperateStatus, OperateInfos;
        let closePromiseProcess = new Promise((resolve, reject) => {
            if (!curInstance.__InstanceCollect_Root.closeEvent) {
                switch (curInstance.__MeTools.ModeLauncherSelect(curInstance.__InstanceCollect_Root.confirmPassData)) {
                    case "http":
                        curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已发出http服务停止指令。", "info");
                        curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore.close((err) => {
                            if (!err) {
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已经停止http网站服务。", "warn");
                                resolve("网站服务器模块、已经停止http网站服务。");
                            } else {
                                err.message_chn = `网站服务器模块、停止http网站服务失败，详情：${err.message}`
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, `网站服务器模块、停止http网站服务失败，详情：${err.message}`, "fail");
                                reject(err);
                            }
                            curInstance.__InstanceCollect_Root.OperateInstance.confirmClear = true;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteControlPoint = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._RootDirPoint = undefined;
                        });
                        break;
                    case "https":
                        curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已发出https服务停止指令。", "info");
                        curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore.close((err) => {
                            if (!err) {
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已经停止https网站服务。", "warn");
                                resolve("网站服务器模块、已经停止https网站服务。");
                            } else {
                                err.message_chn = `网站服务器模块、停止https网站服务失败，详情：${err.message}`
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, `网站服务器模块、停止https网站服务失败，详情：${err.message}`, "fail");
                                reject(err);
                            }
                            curInstance.__InstanceCollect_Root.OperateInstance.confirmClear = true;
                            curInstance.__InstanceCollect_Root.OperateInstance.confirmClear = true;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteControlPoint = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._RootDirPoint = undefined;
                        });
                        break;
                    case "both":
                        curInstance.__InstanceCollect_Root.OperateInstance.confirmClear = [];
                        curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已发出http服务停止指令。", "info");
                        curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已发出https服务停止指令。", "info");
                        curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore[0].close((err) => {
                            if (!err) {
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已经停止http网站服务。", "warn");
                                resolve("网站服务器模块、已经停止http网站服务。");
                            } else {
                                err.message_chn = `网站服务器模块、停止http网站服务失败，详情：${err.message}`
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, `网站服务器模块、停止http网站服务失败，详情：${err.message}`, "fail");
                                reject(err);
                            }
                            curInstance.__InstanceCollect_Root.OperateInstance.confirmClear.push(true);
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteControlPoint = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._RootDirPoint = undefined;
                        });
                        curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore[1].close((err) => {
                            if (!err) {
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, "网站服务器模块、已经停止https网站服务。", "warn");
                                resolve("网站服务器模块、已经停止https网站服务。");
                            } else {
                                err.message_chn = `网站服务器模块、停止https网站服务失败，详情：${err.message}`
                                curInstance.__InstanceCollect_Root.consoleLogStream.addonLog(curInstance, `网站服务器模块、停止https网站服务失败，详情：${err.message}`, "fail");
                                reject(err);
                            }
                            curInstance.__InstanceCollect_Root.OperateInstance.confirmClear.push(true);
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteCore = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._webSiteControlPoint = undefined;
                            curInstance.__InstanceCollect_Root.OperateInstance.webSiteInstance._RootDirPoint = undefined;
                        });
                        break;
                }
            } else {
                reject("未启用服务实例，无需关闭。");
            }
        });
        await closePromiseProcess.then((val) => {
            curInstance.__InstanceCollect_Root.closeEvent = true;
            OperateStatus = true;
            OperateInfos = val;
        }).catch((val) => {
            OperateStatus = false;
            OperateInfos = val;
        });
        if (OperateStatus) {
            return OperateInfos;
        } else {
            throw OperateInfos;
        }
    }
}

module.exports = website;